<?php

class BuildingService extends AbstractService
{

    /**
     * @var BuildingService
     */
    private static $instance;

    /**
     * Get the instance of BuildingService.
     *
     * @return BuildingService
     */
    public static function get()
    {
        if (!self::$instance) {
            self::$instance = new BuildingService();
        }
        return self::$instance;
    }

    /**
     * Construction.
     */
    protected function __construct()
    {
        parent::__construct();
    }

    /**
     * 处理用户创建时的逻辑
     * 1. 地块解锁
     *
     * @param int $userId
     * @param GroundVoList $groundList default null
     * @param BuildingVoList $buildingList default null
     * @return void
     */
    public function handleCreate($userId, $groundList = null, $buildingList = null)
    {
        $initGroundBlockIds = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GAME, GameConst::DEFAULT_GAME_SETTING_KEY, true);
        BuildingService::get()->generateForBlockUnlock($userId, $initGroundBlockIds['initGroundBlockIds'], $groundList, $buildingList);
    }

    /**
     * 处理用户登录时的逻辑
     *
     * @param int $userId
     * @return void
     */
    public function handleLogin($userId)
    {
        // do nothing
    }

    /**
     * 处理 service 对外提供的全量数据
     *
     * @param int $userId
     * @return array
     */
    public function info($userId)
    {
        $buildingList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_BUILDING);
        return array(
            $buildingList
        );
    }

    /**
     * 创建一个 Building 对象
     *
     * @param int $userId
     * @param int $itemDefId
     * @param int $orient
     * @param int $cutRibbon
     * @param int $status
     * @param int $effectiveTime
     * @return BuildingVo $building
     */
    public function create($userId, $itemDefId, $orient, $cutRibbon, $status, $effectiveTime)
    {
        // add decor record
        $building = new BuildingVo(null, // decorId
            $userId, // userId
            $itemDefId, // decorDefId
            $orient, // orient
            $effectiveTime, // effectiveTime
            $cutRibbon, // cutRibbon
            $status, // status
            true);

        return $building;
    }

    /**
     * 将装饰物从背包放到地图上
     *
     * @param int $userId
     * @param int $itemDefId
     * @param array $gridIds [左下gridId， 右上gridId]
     * @param array $blockIds
     * @param int $orient
     * @param int $reqTime
     * @throws SzException 20202
     * @return array(GroundVo, DecorVo)
     */
    public function put($userId, $itemDefId, $gridIds, $blockIds, $orient, $reqTime)
    {
        $buildingSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING, $itemDefId, true);

        // 验证是否物品可使用
        if (!CollectionService::get()->isUnlock($userId, $itemDefId)) {
            throw new SzException(20044, $itemDefId);
        }

        // 验证地块格子有效性
        list($gridId) = $gridIds;
        GroundService::get()->isGridValid($userId, $gridIds, $blockIds, $buildingSetting['position']);

        /**
         * @var BuildingVoList $buildingList
         * @var BuildingVo $building
         */
        $buildingList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_BUILDING);
        $this->verifyBuildingLimit($userId, $itemDefId, $buildingSetting['limit'], $buildingList);

        /**
         * @var GroundVoList $groundList
         * @var GroundVo $ground
         */
        $groundList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_GROUND);
        $ground = $groundList->getElement($gridId, false);

        // 判断地块指定层级是否由道具，或者地块第一级上存在土地。
        if ($ground) {
            $layers = array();
            if (GroundService::get()->isLayerOccupied($ground, $buildingSetting['layer'])) {
                $layers[] = $buildingSetting['layer'];
            }

            // 验证地块 同层级 是否有建筑物或者土地，如果有，则尝试回收。
            for ($i = 0; $i < count($layers); $i++) {
                $decorId = $ground->getLayer($layers[$i]);

                // 获取源数据
                /** @var BuildingVo $originalBuilding */
                $originalBuilding = $buildingList->getElement($decorId);
                $originalBuildingSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING, $originalBuilding->getBuildingDefId(), true);

                // 不可回收物品
                if ($originalBuildingSetting['storable'] == 0) {
                    throw new SzException(20160, $gridId);
                }

                // 第一层存在土地，不可操作回收
                if ($originalBuildingSetting['type'] == GameConst::ITEM_CATEGORY_SOIL) {
                    throw new SzException(20222);
                }

                // 回收装饰物
                $this->takeback($userId, $gridId, $buildingSetting['layer'], $decorId, $originalBuilding, $buildingList, $ground, $groundList);
            }
        }

        // 扣除建筑物道具
        list($itemList) = ItemService::get()->processItems($userId, array($itemDefId => -1));
        SzPersister::get()->setVoList($itemList);

        // 创建建筑物
        /** @var SzMySqlModel $buildingModel */
        $building = $this->create($userId, $itemDefId, $orient, GameConst::DEFAULT_BOOL_OF_TRUE, GameConst::BUILDING_IS_BUILD, $reqTime);
        $buildingModel = SzPersister::get()->getModel(GameConst::ORM_NAME_BUILDING);
        list($affectedRows, $decorId) = $buildingModel->insert($building);
        if ($affectedRows) {
            $building->setDecorId($decorId);
            $building->removeInsertFlag();
            $buildingList->setElement($decorId, $building);
            $buildingModel->setListOfVoCache(array($building), $buildingList->getList());
            SzPersister::get()->addManuallyInsertedResponse($building, $userId, $decorId);
        } else {
            throw new SzException(20202);
        }
        SzPersister::get()->setVoList($buildingList);

        // 更新地块信息
        GroundService::get()->updateGroundWithGridId($userId, $gridId, $buildingSetting['layer'], $decorId, $ground, $groundList, GameConst::GROUND_LAYER_TYPE_DECORATION);
        SzPersister::get()->setVoList($groundList);

        // 更新任务进度
        MissionService::get()->progress($userId, GameConst::ENTITY_ACTION_BUILDING_PUT, GameConst::ENTITY_TYPE_ITEM, $itemDefId, 1);

        return array($ground, $building);
    }

    /**
     * 收回一个装饰品
     *
     * @param int $userId
     * @param int $gridId
     * @param int $girdLayer
     * @param int $decorId
     * @param BuildingVo $building
     * @param BuildingVoList $buildingList
     * @param GroundVo $ground
     * @param GroundVoList $groundList
     * @throws SzException 20203
     * @return array(GroundVo)
     */
    public function takeback($userId, $gridId, $girdLayer, $decorId = null, $building = null, $buildingList = null, $ground = null, $groundList = null)
    {
        if (!$decorId || !$building || !$buildingList || !$ground || !$groundList) {
            list($decorId, $building, $buildingList, $ground, $groundList) = $this->getGroundAndDecorRecord($userId, $gridId, $girdLayer);
        }

        $buildingDefId = $building->getBuildingDefId();
        $buildingSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING, $buildingDefId, true);

        if (!$buildingSetting['storable']) {
            throw new SzException(20203, $buildingSetting['decorDefId']);
        }

        // 删除建筑
        $buildingList->deleteElement($decorId);
        SzPersister::get()->setVoList($buildingList);

        // 清除地块层级
        GroundService::get()->clearGround($buildingSetting['layer'], $ground, $groundList);
        SzPersister::get()->setVoList($groundList);

        // 建筑道具回收进背包
        list($itemList) = ItemService::get()->processItems($userId, array($buildingDefId => 1), null, true);
        SzPersister::get()->setVoList($itemList);

        // 清除建筑的其他附加数据
        $this->clear($userId, $decorId);

        // 数据采集：回收物品
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
            'clientData' => array(
                'tid' => 'Module:Withdraw|action:Withdraw|',
                'item' => $buildingDefId,
                'value' => 1
            )
        ));

        return array($ground);
    }

    /**
     * 摧毁一个装饰物
     *
     * @param int $userId
     * @param int $gridId
     * @param int $girdLayer
     * @param int $decorId
     * @param BuildingVo $building
     * @param BuildingVoList $buildingList
     * @param GroundVo $ground
     * @param GroundVoList $groundList
     * @throws SzException 20203
     * @return array(GroundVo)
     */
    public function destroy($userId, $gridId, $girdLayer, $decorId = null, $building = null, $buildingList = null, $ground = null, $groundList = null)
    {
        if (!$decorId || !$building || !$buildingList || !$ground || !$groundList) {
            list($decorId, $building, $buildingList, $ground, $groundList) = $this->getGroundAndDecorRecord($userId, $gridId, $girdLayer);
        }

        $buildingDefId = $building->getBuildingDefId();
        $buildingSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING, $buildingDefId, true);

        // 删除建筑
        $buildingList->deleteElement($decorId);
        SzPersister::get()->setVoList($buildingList);

        // 清除地块层级
        GroundService::get()->clearGround($buildingSetting['layer'], $ground, $groundList);
        SzPersister::get()->setVoList($groundList);

        // 清除建筑的其他附加数据
        $this->clear($userId, $decorId);

        return array($ground);
    }

    /**
     * 旋转装饰品
     *
     * @param int $userId
     * @param array $gridIds
     * @param array $blockIds
     * @param int $girdLayer
     * @param int $orient
     * @throws SzException 20204
     * @return array(GroundVo)
     */
    public function rotate($userId, $gridIds, $blockIds, $girdLayer, $orient)
    {
        /**
         * @var GroundVo $ground
         * @var BuildingVo $building
         * @var BuildingVoList $buildingList
         */
        list($gridId) = $gridIds;
        list($decorId, $building, $buildingList, $ground) = $this->getGroundAndDecorRecord($userId, $gridId, $girdLayer);
        $buildingDefId = $building->getBuildingDefId();
        $buildingSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING, $buildingDefId, true);

        // 装饰品不可旋转
        if (!$buildingSetting['rotatable']) {
            throw new SzException(20204, $buildingDefId);
        }

        // 装饰品朝向数据不正确
        if ($orient < 0 || $orient > 4) {
            throw new SzException(20204, $buildingDefId);
        }

        // 验证地块有效性
        GroundService::get()->isGridValid($userId, $gridIds, $blockIds, $buildingSetting['position']);

        // 更新装饰物信息
        $building->setOrient($orient);
        $buildingList->updateElement($decorId, $building);
        SzPersister::get()->setVoList($buildingList);

        return array($ground);
    }

    /**
     * 移动装饰品
     *
     * @param int $userId
     * @param int $originGridId
     * @param int $gridLayer
     * @param array $targetGridIds
     * @param array $targetBlockIds
     * @return array
     * @throws SzException
     */
    public function move($userId, $originGridId, $gridLayer, $targetGridIds, $targetBlockIds)
    {
        /**
         * @var GroundVo $ground
         * @var GroundVoList $groundList
         * @var BuildingVo $building
         * @var BuildingVoList $buildingList
         */
        list($decorId, $building, $buildingList, $originGround, $groundList) = $this->getGroundAndDecorRecord($userId, $originGridId, $gridLayer);
        $buildingDefId = $building->getBuildingDefId();
        $buildingSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING, $buildingDefId, true);
        // 目标是否可被移动
        if (!$buildingSetting['movable']) {
            throw new SzException(20227, $buildingDefId);
        }
        // 验证目标地块格子有效性
        list($targetGridId) = $targetGridIds;
        GroundService::get()->isGridValid($userId, $targetGridIds, $targetBlockIds, $buildingSetting['position']);

        /* @var GroundVo $targetGround */
        $targetGround = $groundList->getElement($targetGridId, false);
        GroundService::get()->isLayerOccupied($targetGround, $buildingSetting['layer'], true);

        // 清除源地块上的层级信息，更新目标地块上的层级信息
        GroundService::get()->clearGround($buildingSetting['layer'], $originGround, $groundList);
        GroundService::get()->updateGroundWithGridId($userId, $targetGridId, $buildingSetting['layer'], $decorId, $targetGround, $groundList, GameConst::GROUND_LAYER_TYPE_DECORATION);
        SzPersister::get()->setVoList($groundList);

        return array($originGround, $targetGround);
    }

    /**
     * 装饰物升级
     *
     * @param int $userId
     * @param int $gridId
     * @param int $gridLayer
     * @param int $reqTime
     * @throws SzException 20206
     * @return array(GroundVo, BuildingVo)
     */
    public function upgrade($userId, $gridId, $gridLayer, $reqTime)
    {
        /**
         * @var GroundVo $ground
         * @var BuildingVo $building
         * @var BuildingVoList $buildingList
         */
        list($decorId, $building, $buildingList, $ground) = $this->getGroundAndDecorRecord($userId, $gridId, $gridLayer);
        $buildingDefId = $building->getBuildingDefId();
        $buildingSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING, $buildingDefId, true);
        $upgradeSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING_UPGRADE, $buildingDefId, true);

        // 装饰品无法升级
        $upgradeBuildingDefId = $upgradeSetting['upgradeBuildingId'];
        if ($upgradeBuildingDefId < 0 || UserService::get()->getUserLevel($userId) < $upgradeSetting['playerLevel']) {
            throw new SzException(20206, $buildingDefId);
        }

        $upgradeFinishSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING_UPGRADE, $upgradeBuildingDefId, true, false);

        // 扣除升级道具
        $tid = '';
        for ($i = 1; $i <= 3; $i++) {
            GameService::get()->handleEntity($userId, $upgradeSetting['upgradeItemId_' . $i], $upgradeSetting['upgradeItemCount_' . $i], true);
            if ($upgradeSetting['upgradeItemId_' . $i] != -1 && $upgradeSetting['upgradeItemCount_' . $i] != -1) {
                $tid .= $upgradeSetting['upgradeItemId_' . $i] . ':' . abs($upgradeSetting['upgradeItemCount_' . $i]) . '|';
            }
        }
        CommonFunctionService::get()->reportResourceUseDetails(GameConst::FUNC_BUILDING_UPGRADE,$userId,0,$tid,'upgrade');//消耗资源上报

        // 更新建筑信息
        $building->setBuildingDefId($upgradeBuildingDefId);
        $building->setStatus(GameConst::BUILDING_IS_BUILD);
        $building->setCutRibbon(($upgradeFinishSetting['cutRibbon'] == 1) ? GameConst::DEFAULT_BOOL_OF_FALSE : GameConst::DEFAULT_BOOL_OF_TRUE);
        $building->setEffectiveTime($reqTime + $upgradeSetting['upgradeTime']);
        $buildingList->updateElement($decorId, $building);
        SzPersister::get()->setVoList($buildingList);

        // 获得建造升级经验
        if ($upgradeFinishSetting['cutRibbon'] == 0) {
            UserService::get()->updateExp($userId, $upgradeFinishSetting['exp']);
        }

        // 数据采集: 货仓升级花仓升级
        list($buildingType) = EntityService::get()->parseItemId($buildingDefId);
        switch ($buildingType) {
            case GameConst::ITEM_TYPE_SELL_GOODS:
                $module = 'flowershop';
                $action = 'unlock';
                $inputItem = $module . '_' . $action . '&' . $buildingDefId;
                $upgradeTid = 'objective:flowershop_unlock|item:' . $buildingDefId . '|';
                $upgradeItem = 'fromlevel:|changelev:unlock|targetlev:|';
                break;
            case GameConst::ITEM_TYPE_SOIL_NORMAL;
            case GameConst::ITEM_TYPE_SOIL_WATER:
                $module = 'farm';
                $action = 'unlock';
                $inputItem = $module . '_' . $action . '&' . $buildingDefId;
                $upgradeTid = '';
                $upgradeItem = '';
                break;
            case GameConst::ITEM_TYPE_WAREHOUSE_BUILDING;
            case GameConst::ITEM_TYPE_WAREHOUSE_FLOWER:
                $warehouseSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING_WAREHOUSE, null, true);
                $module = ($buildingType == GameConst::ITEM_TYPE_WAREHOUSE_BUILDING) ? 'Warehouse' : 'Storage';
                $action = 'upgrade';
                $inputItem = $module . '_' . $action;
                // 数据采集：仓库/花仓扩张
                $chgLv = $warehouseSettings[$upgradeBuildingDefId]['itemLimit'] - $warehouseSettings[$buildingDefId]['itemLimit'];

                $upgradeTid = 'objective:' . $module . '|item:expand|';
                $upgradeItem = 'fromlevel:' . $warehouseSettings[$buildingDefId]['itemLimit'] . '|changelev:' . $chgLv . '|targetlev:|' . $warehouseSettings[$upgradeBuildingDefId]['itemLimit'];
                $buildingDefId = $warehouseSettings[$upgradeBuildingDefId]['itemLimit'];
                break;
            default:
                $module = '';
                $action = '';
                $inputItem = 'building_complete';
                $upgradeTid = 'objective:building|item:building|';
                $upgradeItem = 'fromlevel:|changelev:unlock|targetlev:|';
                break;
        }

        if ($module) {
            // 数据采集：解锁地块
            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
                'clientData' => array(
                    'tid' => 'Module:' . $module . '|action:' . $action . '|',
                    'item' => $buildingDefId,
                    'value' => 1
                )
            ));
        }
        if ($inputItem && $tid) {
            // 数据采集：解锁地块消耗
            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_INPUT, array(
                'clientData' => array(
                    'tid' => $tid,
                    'item' => $inputItem
                )
            ));
        }

        if ($upgradeTid && $upgradeItem) {
            // 数据采集：升级
            StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_UPGRADE, array(
                'clientData' => array(
                    'tid' => $upgradeTid,
                    'item' => $upgradeItem,
                    'value' => 1
                )
            ));
        }
        return array($ground, $building);
    }

    /**
     * 装饰物剪彩
     *
     * @param int $userId
     * @param int $gridId
     * @param int $gridLayer
     * @param int $reqTime
     * @throws SzException 20207
     * @return array(GroundVo, BuildingVo)
     */
    public function cutRibbon($userId, $gridId, $gridLayer, $reqTime)
    {
        /**
         * @var GroundVo $ground
         * @var BuildingVo $building
         * @var BuildingVoList $buildingList
         */
        list($decorId, $building, $buildingList, $ground) = $this->getGroundAndDecorRecord($userId, $gridId, $gridLayer);
        $buildingDefId = $building->getBuildingDefId();
        $buildingSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING, $buildingDefId, true);
        $upgradeSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING_UPGRADE, $buildingDefId, true);

        // 装饰品无法剪彩
        if ($building->getCutRibbon() == GameConst::DEFAULT_BOOL_OF_TRUE || $building->getEffectiveTime() > $reqTime) {
            throw new SzException(20207, $buildingDefId);
        }

        // 更新建筑信息
        $building->setCutRibbon(GameConst::DEFAULT_BOOL_OF_TRUE);
        $buildingList->updateElement($decorId, $building);
        SzPersister::get()->setVoList($buildingList);

        // 获得剪彩经验
        if ($upgradeSetting['cutRibbon'] == 1) {
            GameService::get()->handleEntity($userId, GameConst::ENTITY_ACTION_GET_EXP, $upgradeSetting['exp'], true);
            CommonFunctionService::get()->reportResourceUseDetails(99999990,$userId,1,GameConst::ENTITY_ACTION_GET_EXP.':'.$upgradeSetting['exp'].'|','cutRibbon');//获得资源上报
        }

        // 数据采集：建造完成
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
            'clientData' => array(
                'tid' => 'Module:building|action:complete|',
                'item' => $buildingDefId,
                'value' => 1
            )
        ));

        // 数据采集：建造完成资源产出
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_RESOURCE_OUTPUT, array(
            'clientData' => array(
                'tid' => GameConst::ENTITY_ACTION_GET_EXP . ':' . $upgradeSetting['exp'] . '|',
                'item' => 'building_complete',
                'value' => 1
            )
        ));

        return array($ground, $building);
    }

    /**
     * 装饰物加速建造
     *
     * @param int $userId
     * @param int $gridId
     * @param int $gridLayer
     * @param int $cliCost
     * @param int $reqTime
     * @throws SzException 20206
     * @return boolean
     */
    public function payForSpeedUp($userId, $gridId, $gridLayer, $cliCost, $reqTime)
    {
        /**
         * @var GroundVo $ground
         * @var BuildingVo $building
         * @var BuildingVoList $buildingList
         */
        list($decorId, $building, $buildingList) = $this->getGroundAndDecorRecord($userId, $gridId, $gridLayer);
        $buildingDefId = $building->getBuildingDefId();

        // FIXME 加速建造的加速花费应当是找前一个buildingDefId的，这里和策划约定，建造前后的两个建筑的加速花费id是一致的，暂时先如此处理
        $upgradeSetting = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING_UPGRADE, $buildingDefId, true);

        // 扣除加速花费
        GameService::get()->handleSpeedUp($userId, $upgradeSetting['coolDownId'], $building->getEffectiveTime(), $reqTime, 'building_CD_' . $buildingDefId, $cliCost);

        // 加速建造
        $building->setEffectiveTime($reqTime);
        $buildingList->updateElement($decorId, $building);
        SzPersister::get()->setVoList($buildingList);

        // 数据采集：清除建筑/机器CD
        StatisticsService::get()->saveLog($userId, GameConst::LOG_TYPE_USER_ORBIT, array(
            'clientData' => array(
                'tid' => 'Module:building|action:CD|',
                'item' => $buildingDefId,
                'value' => 1
            )
        ));

        return true;
    }

    /**
     * 通过解锁地块初始化该地块上的装饰物
     *
     * @param int $userId
     * @param array $blockIds
     * @param GroundVoList $groundList default null
     * @param BuildingVoList $buildingList default null
     * @return void
     */
    public function generateForBlockUnlock($userId, $blockIds, $groundList = null, $buildingList = null)
    {
        $reqTime = GameService::get()->getReqTime();
        $buildingSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_BUILDING, null, true);
        $blockObjectSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GROUND_OBJECT, null, true);
        $blockResourceSettings = SzConfig::get()->loadAppConfig(GameConst::CONFIG_GROUND_RESOURCE, null, true);

        if (is_null($groundList)) {
            $groundList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_GROUND);
        }
        if (is_null($buildingList)) {
            $buildingList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_BUILDING);
        }

        /**
         * <pre>
         *  array(
         *      $buildingDefId => array($orient => array($gridId, ...))
         *      , ...
         *  )
         * </pre>
         */
        $gridMap = array();
        foreach ($blockIds as $_blockId) {
            // 未找到地块初始化配置，跳过
            if (!SzUtility::checkArrayKey($_blockId, $blockObjectSettings)) {
                continue;
            }

            // 解锁地块后，自动获得地块上的装饰物
            foreach ($blockObjectSettings[$_blockId]['objectIdArray'] as $_objectId) {
                if (!SzUtility::checkArrayKey($_objectId, $blockResourceSettings)) {
                    continue;
                }

                $buildingDefId = $blockResourceSettings[$_objectId]['itemDefId'];
                $orient = $blockResourceSettings[$_objectId]['direction'];
                $gridId = GroundService::get()->parseCoordinate($blockResourceSettings[$_objectId]['anchorX'], $blockResourceSettings[$_objectId]['anchorY']);

                // 更新gridMapOfDecor
                if (!SzUtility::checkArrayKey($buildingDefId, $gridMap)) {
                    $gridMap[$buildingDefId] = array();
                    $gridMap[$buildingDefId][$orient] = array();
                }
                $gridMap[$buildingDefId][$orient][] = $gridId;

                // 初始化building
                $building = $this->create($userId, $buildingDefId, $orient, GameConst::DEFAULT_BOOL_OF_TRUE, GameConst::BUILDING_IS_BUILD, $reqTime);
                $buildingList->addElement($building);
            }
        }

        // 统一处理装饰物数据, 并手动持久化
        if (count($buildingList->getInsertList()) > 0) {
            // 获取持久化前的 decorId，然后手动持久化
            $originIds = array_keys($buildingList->getList());
            $buildingList->persist();

            // 清除建筑的缓存，并重新获取
            $buildingModel = SzPersister::get()->getModel(GameConst::ORM_NAME_BUILDING);
            SzSystemCache::cache(SzSystemCache::MODEL_VO_LIST_INSTANCE, "{$buildingModel->getVoListClassName()}{$userId}{$userId}");
            $buildingList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_BUILDING);

            // 获取持久化后的 decorId，并比对差异，得到刚才新创建的 decorId
            $currentIds = array_keys($buildingList->getList());
            $diffIds = array_diff($currentIds, $originIds);

            // 根据building的自增长id，逐行处理ground数据
            foreach ($diffIds as $_decorId) {
                /** @var BuildingVo $building */
                $building = $buildingList->getElement($_decorId);
                SzPersister::get()->addManuallyInsertedResponse($building, $userId, $_decorId);

                $orient = $building->getOrient();
                $buildingDefId = $building->getBuildingDefId();

                if (!SzUtility::checkArrayKey($buildingDefId, $gridMap) || !SzUtility::checkArrayKey($orient, $gridMap[$buildingDefId]) || !SzUtility::checkArrayKey($buildingDefId,
                        $buildingSettings)
                ) {
                    continue;
                }

                $gridId = array_shift($gridMap[$buildingDefId][$orient]);
                if (is_null($gridId)) {
                    continue;
                }

                // 更新地块信息
                $ground = $groundList->getElement($gridId, false);
                GroundService::get()->updateGroundWithGridId($userId, $gridId, $buildingSettings[$buildingDefId]['layer'], $_decorId, $ground, $groundList, GameConst::GROUND_LAYER_TYPE_DECORATION);

                // 特殊建筑
                switch ($buildingSettings[$buildingDefId]['type']) {
                    case GameConst::ITEM_CATEGORY_WAREHOUSE:
                        WarehouseService::get()->init($userId, $buildingDefId, $_decorId);
                        break;
                }
            }

            SzPersister::get()->setVoList($groundList);
        }
    }

    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    //-* UTIL FUNCTION
    //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
    /**
     * Check & get ground & decor records.
     *
     * @param int $userId
     * @param int $gridId
     * @param int $gridLayer
     * @param GroundVoList $groundList default null
     * @return array
     * <pre>
     * array($decorId, BuildingVo, BuildingVoList, GroundVo, GroundVoList)
     * </pre>
     */
    public function getGroundAndDecorRecord($userId, $gridId, $gridLayer, $groundList = null)
    {
        /**
         * @var GroundVo $ground
         */
        list($decorId, $ground, $groundList) = GroundService::get()->getGroundRecord($userId, $gridId, $gridLayer, GameConst::GROUND_LAYER_TYPE_DECORATION, $groundList);

        /**
         * @var BuildingVoList $buildingList
         * @var BuildingVo $building
         */
        $buildingList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_BUILDING);
        $building = $buildingList->getElement($decorId);

        return array($decorId, $building, $buildingList, $ground, $groundList);
    }

    /**
     * 验证装饰物摆放上限
     *
     * @param int $userId
     * @param int $itemDefId
     * @param array $limitArray , eg: {"1": 30, "5": 50}
     * @param BuildingVoList $buildingList default null
     * @throws SzException 20201
     * @return void
     */
    private function verifyBuildingLimit($userId, $itemDefId, $limitArray, $buildingList = null)
    {
        $buildingCount = $this->getBuildingCount($userId, $itemDefId, $buildingList);
        $userLevel = UserService::get()->getUserLevel($userId);

        $limit = 0;
        ksort($limitArray);
        foreach ($limitArray as $_level => $_value) {
            if ($userLevel < $_level) {
                break;
            }
            $limit = $_value;
        }

        if ($limit <= $buildingCount) {
            throw new SzException(20201, array($itemDefId, $buildingCount, $limit));
        }
    }

    /**
     * 获取指定建筑数量
     *
     * @param int $userId
     * @param int $itemDefId
     * @param BuildingVoList $buildingList default null
     * @return int
     */
    public function getBuildingCount($userId, $itemDefId, $buildingList = null)
    {
        if (!$buildingList) {
            $buildingList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_BUILDING);
        }
        return $buildingList->getElementsCountByItemDefId($itemDefId);
    }

    /**
     * 删除建筑同时清除建筑的附加表
     *
     * @param int $userId
     * @param int $decorId
     */
    private function clear($userId, $decorId)
    {
        /**
         * @var GoodsVoList $goodsList
         */
        $goodsList = SzPersister::get()->getVoList($userId, GameConst::ORM_NAME_GOODS);
        $goodsList->deleteElement($decorId, false);
        SzPersister::get()->setVoList($goodsList);
    }
}
